/*
Copyright 2023 The Radius Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v20231001preview

import (
	"encoding/json"
	"testing"

	v1 "github.com/radius-project/radius/pkg/armrpc/api/v1"
	"github.com/radius-project/radius/pkg/corerp/datamodel"
	rpv1 "github.com/radius-project/radius/pkg/rp/v1"
	"github.com/radius-project/radius/test/testutil"
	"github.com/radius-project/radius/test/testutil/resourcetypeutil"

	"github.com/stretchr/testify/require"
)

func TestGatewayConvertVersionedToDataModel(t *testing.T) {
	// arrange
	rawPayload := testutil.ReadFixture("gatewayresource.json")
	r := &GatewayResource{}
	err := json.Unmarshal(rawPayload, r)
	require.NoError(t, err)

	// act
	dm, err := r.ConvertTo()

	// assert
	require.NoError(t, err)
	gw := dm.(*datamodel.Gateway)
	require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/gateways/gateway0", gw.ID)
	require.Equal(t, "gateway0", gw.Name)
	require.Equal(t, "Applications.Core/gateways", gw.Type)
	require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Applications.Core/applications/app0", gw.Properties.Application)
	require.Equal(t, "myapp.mydomain.com", gw.Properties.Hostname.FullyQualifiedHostname)
	require.Equal(t, "myprefix", gw.Properties.Hostname.Prefix)
	require.Equal(t, "mydestination", gw.Properties.Routes[0].Destination)
	require.Equal(t, "mypath", gw.Properties.Routes[0].Path)
	require.Equal(t, "myreplaceprefix", gw.Properties.Routes[0].ReplacePrefix)
	require.False(t, gw.Properties.Routes[0].EnableWebsockets)
	require.Equal(t, "http://myprefix.myapp.mydomain.com", gw.Properties.URL)
	require.Equal(t, []rpv1.OutputResource(nil), gw.Properties.Status.OutputResources)
	require.Equal(t, "2023-10-01-preview", gw.InternalMetadata.UpdatedAPIVersion)
}

func TestGatewayConvertDataModelToVersioned(t *testing.T) {
	// arrange
	rawPayload := testutil.ReadFixture("gatewayresourcedatamodel.json")
	r := &datamodel.Gateway{}
	err := json.Unmarshal(rawPayload, r)
	require.NoError(t, err)

	// act
	versioned := &GatewayResource{}
	err = versioned.ConvertFrom(r)

	// assert
	require.NoError(t, err)
	require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/gateways/gateway0", *versioned.ID)
	require.Equal(t, "gateway0", *versioned.Name)
	require.Equal(t, "Applications.Core/gateways", *versioned.Type)
	require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Applications.Core/applications/app0", *versioned.Properties.Application)
	require.Equal(t, "myapp.mydomain.com", *versioned.Properties.Hostname.FullyQualifiedHostname)
	require.Equal(t, "myprefix", *versioned.Properties.Hostname.Prefix)
	require.Equal(t, "myreplaceprefix", *versioned.Properties.Routes[0].ReplacePrefix)
	require.False(t, *versioned.Properties.Routes[0].EnableWebsockets)
	require.Equal(t, "mypath", *versioned.Properties.Routes[0].Path)
	require.Equal(t, "http://myprefix.myapp.mydomain.com", *versioned.Properties.URL)
	require.Equal(t, resourcetypeutil.MustPopulateResourceStatus(&ResourceStatus{}), versioned.Properties.Status)
}

func TestTimeoutPolicyGatewayConvertVersionedToDataModel(t *testing.T) {
	// arrange
	rawPayload := testutil.ReadFixture("gatewayresourcedatamodel-with-gatewaytimeout.json")
	r := &GatewayResource{}
	err := json.Unmarshal(rawPayload, r)
	require.NoError(t, err)

	// act
	dm, err := r.ConvertTo()

	// assert
	require.NoError(t, err)
	gw := dm.(*datamodel.Gateway)
	require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/gateways/gateway0", gw.ID)
	require.Equal(t, "gateway0", gw.Name)
	require.Equal(t, "Applications.Core/gateways", gw.Type)
	require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Applications.Core/applications/app0", gw.Properties.Application)
	require.Equal(t, "myapp.mydomain.com", gw.Properties.Hostname.FullyQualifiedHostname)
	require.Equal(t, "myprefix", gw.Properties.Hostname.Prefix)
	require.Equal(t, "mydestination", gw.Properties.Routes[0].Destination)
	require.Equal(t, "mypath", gw.Properties.Routes[0].Path)
	require.Equal(t, "myreplaceprefix", gw.Properties.Routes[0].ReplacePrefix)
	require.False(t, gw.Properties.Routes[0].EnableWebsockets)
	require.Equal(t, "30s", gw.Properties.Routes[0].TimeoutPolicy.Request)
	require.Equal(t, "20s", gw.Properties.Routes[0].TimeoutPolicy.BackendRequest)
	require.Equal(t, "http://myprefix.myapp.mydomain.com", gw.Properties.URL)
	require.Equal(t, []rpv1.OutputResource(nil), gw.Properties.Status.OutputResources)
	require.Equal(t, "2023-10-01-preview", gw.InternalMetadata.UpdatedAPIVersion)
}

func TestTimeoutPolicyGatewayConvertDataModelToVersioned(t *testing.T) {
	// arrange
	rawPayload := testutil.ReadFixture("gatewayresourcedatamodel-with-gatewaytimeout.json")
	r := &datamodel.Gateway{}
	err := json.Unmarshal(rawPayload, r)
	require.NoError(t, err)

	// act
	versioned := &GatewayResource{}
	err = versioned.ConvertFrom(r)

	// assert
	require.NoError(t, err)
	require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/gateways/gateway0", *versioned.ID)
	require.Equal(t, "gateway0", *versioned.Name)
	require.Equal(t, "Applications.Core/gateways", *versioned.Type)
	require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Applications.Core/applications/app0", *versioned.Properties.Application)
	require.Equal(t, "myapp.mydomain.com", *versioned.Properties.Hostname.FullyQualifiedHostname)
	require.Equal(t, "myprefix", *versioned.Properties.Hostname.Prefix)
	require.Equal(t, "myreplaceprefix", *versioned.Properties.Routes[0].ReplacePrefix)
	require.Equal(t, "30s", *versioned.Properties.Routes[0].TimeoutPolicy.Request)
	require.Equal(t, "20s", *versioned.Properties.Routes[0].TimeoutPolicy.BackendRequest)
	require.False(t, *versioned.Properties.Routes[0].EnableWebsockets)
	require.Equal(t, "mypath", *versioned.Properties.Routes[0].Path)
	require.Equal(t, "http://myprefix.myapp.mydomain.com", *versioned.Properties.URL)
	require.Equal(t, resourcetypeutil.MustPopulateResourceStatus(&ResourceStatus{}), versioned.Properties.Status)
}

func TestGatewaySSLPassthroughConvertVersionedToDataModel(t *testing.T) {
	// arrange
	rawPayload := testutil.ReadFixture("gatewayresource-with-sslpassthrough.json")
	r := &GatewayResource{}
	err := json.Unmarshal(rawPayload, r)
	require.NoError(t, err)

	// act
	dm, err := r.ConvertTo()

	// assert
	require.NoError(t, err)
	gw := dm.(*datamodel.Gateway)
	require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/gateways/gateway0", gw.ID)
	require.Equal(t, "gateway0", gw.Name)
	require.Equal(t, "Applications.Core/gateways", gw.Type)
	require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Applications.Core/applications/app0", gw.Properties.Application)
	require.Equal(t, "myapp.mydomain.com", gw.Properties.Hostname.FullyQualifiedHostname)
	require.Equal(t, "myprefix", gw.Properties.Hostname.Prefix)
	require.Equal(t, "mydestination", gw.Properties.Routes[0].Destination)
	require.Equal(t, "mypath", gw.Properties.Routes[0].Path)
	require.Equal(t, "myreplaceprefix", gw.Properties.Routes[0].ReplacePrefix)
	require.False(t, gw.Properties.Routes[0].EnableWebsockets)
	require.Equal(t, "http://myprefix.myapp.mydomain.com", gw.Properties.URL)
	require.Equal(t, []rpv1.OutputResource(nil), gw.Properties.Status.OutputResources)
	require.Equal(t, "2023-10-01-preview", gw.InternalMetadata.UpdatedAPIVersion)
	require.Equal(t, true, gw.Properties.TLS.SSLPassthrough)
}

func TestGatewaySSLPassthroughConvertDataModelToVersioned(t *testing.T) {
	// arrange
	rawPayload := testutil.ReadFixture("gatewayresourcedatamodel-with-sslpassthrough.json")
	r := &datamodel.Gateway{}
	err := json.Unmarshal(rawPayload, r)
	require.NoError(t, err)

	// act
	versioned := &GatewayResource{}
	err = versioned.ConvertFrom(r)

	// assert
	require.NoError(t, err)
	require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/gateways/gateway0", *versioned.ID)
	require.Equal(t, "gateway0", *versioned.Name)
	require.Equal(t, "Applications.Core/gateways", *versioned.Type)
	require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Applications.Core/applications/app0", *versioned.Properties.Application)
	require.Equal(t, "myapp.mydomain.com", *versioned.Properties.Hostname.FullyQualifiedHostname)
	require.Equal(t, "myprefix", *versioned.Properties.Hostname.Prefix)
	require.Equal(t, "mypath", *versioned.Properties.Routes[0].Path)
	require.Equal(t, "myreplaceprefix", *versioned.Properties.Routes[0].ReplacePrefix)
	require.False(t, *versioned.Properties.Routes[0].EnableWebsockets)
	require.Equal(t, "http://myprefix.myapp.mydomain.com", *versioned.Properties.URL)
	require.Equal(t, resourcetypeutil.MustPopulateResourceStatus(&ResourceStatus{}), versioned.Properties.Status)
	require.Equal(t, true, *versioned.Properties.TLS.SSLPassthrough)
}

func TestGatewayEnableWebsocketsConvertVersionedToDataModel(t *testing.T) {
	// arrange
	rawPayload := testutil.ReadFixture("gatewayresourcedatamodel-with-enablewebsockets.json")
	r := &GatewayResource{}
	err := json.Unmarshal(rawPayload, r)
	require.NoError(t, err)

	// act
	dm, err := r.ConvertTo()

	// assert
	require.NoError(t, err)
	gw := dm.(*datamodel.Gateway)
	require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/gateways/gateway0", gw.ID)
	require.Equal(t, "gateway0", gw.Name)
	require.Equal(t, "Applications.Core/gateways", gw.Type)
	require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Applications.Core/applications/app0", gw.Properties.Application)
	require.Equal(t, "myapp.mydomain.com", gw.Properties.Hostname.FullyQualifiedHostname)
	require.Equal(t, "myprefix", gw.Properties.Hostname.Prefix)
	require.Equal(t, "mydestination", gw.Properties.Routes[0].Destination)
	require.Equal(t, "mypath", gw.Properties.Routes[0].Path)
	require.Equal(t, "myreplaceprefix", gw.Properties.Routes[0].ReplacePrefix)
	require.True(t, gw.Properties.Routes[0].EnableWebsockets)
	require.Equal(t, "http://myprefix.myapp.mydomain.com", gw.Properties.URL)
	require.Equal(t, []rpv1.OutputResource(nil), gw.Properties.Status.OutputResources)
	require.Equal(t, "2023-10-01-preview", gw.InternalMetadata.UpdatedAPIVersion)
	require.Equal(t, true, gw.Properties.TLS.SSLPassthrough)
}

func TestGatewayEnableWebsocketsConvertDataModelToVersioned(t *testing.T) {
	// arrange
	rawPayload := testutil.ReadFixture("gatewayresourcedatamodel-with-enablewebsockets.json")
	r := &datamodel.Gateway{}
	err := json.Unmarshal(rawPayload, r)
	require.NoError(t, err)

	// act
	versioned := &GatewayResource{}
	err = versioned.ConvertFrom(r)

	// assert
	require.NoError(t, err)
	require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/gateways/gateway0", *versioned.ID)
	require.Equal(t, "gateway0", *versioned.Name)
	require.Equal(t, "Applications.Core/gateways", *versioned.Type)
	require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Applications.Core/applications/app0", *versioned.Properties.Application)
	require.Equal(t, "myapp.mydomain.com", *versioned.Properties.Hostname.FullyQualifiedHostname)
	require.Equal(t, "myprefix", *versioned.Properties.Hostname.Prefix)
	require.Equal(t, "mypath", *versioned.Properties.Routes[0].Path)
	require.Equal(t, "myreplaceprefix", *versioned.Properties.Routes[0].ReplacePrefix)
	require.True(t, *versioned.Properties.Routes[0].EnableWebsockets)
	require.Equal(t, "http://myprefix.myapp.mydomain.com", *versioned.Properties.URL)
	require.Equal(t, resourcetypeutil.MustPopulateResourceStatus(&ResourceStatus{}), versioned.Properties.Status)
	require.Equal(t, true, *versioned.Properties.TLS.SSLPassthrough)
}

func TestGatewayTLSTerminationConvertVersionedToDataModel(t *testing.T) {
	// arrange
	rawPayload := testutil.ReadFixture("gatewayresource-with-tlstermination.json")
	r := &GatewayResource{}
	err := json.Unmarshal(rawPayload, r)
	require.NoError(t, err)

	// act
	dm, err := r.ConvertTo()

	// assert
	require.NoError(t, err)
	gw := dm.(*datamodel.Gateway)
	require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/gateways/gateway0", gw.ID)
	require.Equal(t, "gateway0", gw.Name)
	require.Equal(t, "Applications.Core/gateways", gw.Type)
	require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Applications.Core/applications/app0", gw.Properties.Application)
	require.Equal(t, "myapp.mydomain.com", gw.Properties.Hostname.FullyQualifiedHostname)
	require.Equal(t, "myprefix", gw.Properties.Hostname.Prefix)
	require.Equal(t, "mydestination", gw.Properties.Routes[0].Destination)
	require.Equal(t, "mypath", gw.Properties.Routes[0].Path)
	require.Equal(t, "myreplaceprefix", gw.Properties.Routes[0].ReplacePrefix)
	require.False(t, gw.Properties.Routes[0].EnableWebsockets)
	require.Equal(t, "http://myprefix.myapp.mydomain.com", gw.Properties.URL)
	require.Equal(t, []rpv1.OutputResource(nil), gw.Properties.Status.OutputResources)
	require.Equal(t, "2023-10-01-preview", gw.InternalMetadata.UpdatedAPIVersion)
	require.Equal(t, "secretname", gw.Properties.TLS.CertificateFrom)
	require.Equal(t, datamodel.TLSMinVersion13, gw.Properties.TLS.MinimumProtocolVersion)
}

func TestGatewayTLSTerminationConvertDataModelToVersioned(t *testing.T) {
	// arrange
	rawPayload := testutil.ReadFixture("gatewayresourcedatamodel-with-tlstermination.json")
	r := &datamodel.Gateway{}
	err := json.Unmarshal(rawPayload, r)
	require.NoError(t, err)

	// act
	versioned := &GatewayResource{}
	err = versioned.ConvertFrom(r)

	// assert
	require.NoError(t, err)
	require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/gateways/gateway0", *versioned.ID)
	require.Equal(t, "gateway0", *versioned.Name)
	require.Equal(t, "Applications.Core/gateways", *versioned.Type)
	require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Applications.Core/applications/app0", *versioned.Properties.Application)
	require.Equal(t, "myapp.mydomain.com", *versioned.Properties.Hostname.FullyQualifiedHostname)
	require.Equal(t, "myprefix", *versioned.Properties.Hostname.Prefix)
	require.Equal(t, "mypath", *versioned.Properties.Routes[0].Path)
	require.Equal(t, "myreplaceprefix", *versioned.Properties.Routes[0].ReplacePrefix)
	require.False(t, *versioned.Properties.Routes[0].EnableWebsockets)
	require.Equal(t, "http://myprefix.myapp.mydomain.com", *versioned.Properties.URL)
	require.Equal(t, resourcetypeutil.MustPopulateResourceStatus(&ResourceStatus{}), versioned.Properties.Status)
	require.Equal(t, "secretname", *versioned.Properties.TLS.CertificateFrom)
	require.Equal(t, TLSMinVersionTls13, *versioned.Properties.TLS.MinimumProtocolVersion)
}

func TestGatewayTLSTerminationConvertVersionedToDataModel_NoMinProtocolVersion(t *testing.T) {
	// arrange
	rawPayload := testutil.ReadFixture("gatewayresource-with-tlstermination-nominprotocolversion.json")
	r := &GatewayResource{}
	err := json.Unmarshal(rawPayload, r)
	require.NoError(t, err)

	// act
	dm, err := r.ConvertTo()

	// assert
	require.NoError(t, err)
	gw := dm.(*datamodel.Gateway)
	require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/gateways/gateway0", gw.ID)
	require.Equal(t, "gateway0", gw.Name)
	require.Equal(t, "Applications.Core/gateways", gw.Type)
	require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Applications.Core/applications/app0", gw.Properties.Application)
	require.Equal(t, "myapp.mydomain.com", gw.Properties.Hostname.FullyQualifiedHostname)
	require.Equal(t, "myprefix", gw.Properties.Hostname.Prefix)
	require.Equal(t, "mydestination", gw.Properties.Routes[0].Destination)
	require.Equal(t, "mypath", gw.Properties.Routes[0].Path)
	require.Equal(t, "myreplaceprefix", gw.Properties.Routes[0].ReplacePrefix)
	require.False(t, gw.Properties.Routes[0].EnableWebsockets)
	require.Equal(t, "http://myprefix.myapp.mydomain.com", gw.Properties.URL)
	require.Equal(t, []rpv1.OutputResource(nil), gw.Properties.Status.OutputResources)
	require.Equal(t, "2023-10-01-preview", gw.InternalMetadata.UpdatedAPIVersion)
	require.Equal(t, "secretname", gw.Properties.TLS.CertificateFrom)
	require.Equal(t, datamodel.DefaultTLSMinVersion, gw.Properties.TLS.MinimumProtocolVersion)
}

func TestGatewayTLSTerminationConvertDataModelToVersioned_NoMinProtocolVersion(t *testing.T) {
	// arrange
	rawPayload := testutil.ReadFixture("gatewayresourcedatamodel-with-tlstermination-nominprotocolversion.json")
	r := &datamodel.Gateway{}
	err := json.Unmarshal(rawPayload, r)
	require.NoError(t, err)

	// act
	versioned := &GatewayResource{}
	err = versioned.ConvertFrom(r)

	// assert
	require.NoError(t, err)
	require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/radius-test-rg/providers/Applications.Core/gateways/gateway0", *versioned.ID)
	require.Equal(t, "gateway0", *versioned.Name)
	require.Equal(t, "Applications.Core/gateways", *versioned.Type)
	require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/providers/Applications.Core/applications/app0", *versioned.Properties.Application)
	require.Equal(t, "myapp.mydomain.com", *versioned.Properties.Hostname.FullyQualifiedHostname)
	require.Equal(t, "myprefix", *versioned.Properties.Hostname.Prefix)
	require.Equal(t, "mypath", *versioned.Properties.Routes[0].Path)
	require.Equal(t, "myreplaceprefix", *versioned.Properties.Routes[0].ReplacePrefix)
	require.False(t, *versioned.Properties.Routes[0].EnableWebsockets)
	require.Equal(t, "http://myprefix.myapp.mydomain.com", *versioned.Properties.URL)
	require.Equal(t, resourcetypeutil.MustPopulateResourceStatus(&ResourceStatus{}), versioned.Properties.Status)
	require.Equal(t, "secretname", *versioned.Properties.TLS.CertificateFrom)
	require.Equal(t, TLSMinVersionTls12, *versioned.Properties.TLS.MinimumProtocolVersion)
}

func TestGatewayConvertFromValidation(t *testing.T) {
	validationTests := []struct {
		src v1.DataModelInterface
		err error
	}{
		{&resourcetypeutil.FakeResource{}, v1.ErrInvalidModelConversion},
		{nil, v1.ErrInvalidModelConversion},
	}

	for _, tc := range validationTests {
		versioned := &GatewayResource{}
		err := versioned.ConvertFrom(tc.src)
		require.ErrorAs(t, tc.err, &err)
	}
}
